/**
 * 
 */
package org.swing.utility.jai.util;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.log4j.Logger;
import org.swing.utility.jai.bean.ImageSize;
import org.swing.utility.jai.imp.ImageAction;
import org.swing.utility.jai.magic.ImageMagickCmd;

import com.revolsys.io.FileUtil;
import com.revolsys.io.filter.DirectoryFilenameFilter;
import com.revolsys.io.filter.ExtensionFilenameFilter;
import com.revolsys.io.filter.MultipleFilenameFilter;
import com.revolsys.io.filter.NameFilenameFilter;
/**
 * @author Quynh Nhu
 *
 */
public class ImageResizer implements Runnable {
	  private MultipleFilenameFilter directoryFilter;

	  private SortedSet imageQueue = new TreeSet();

	  private List imageSizes;

	  private ImageAction imageAction = new ImageMagickCmd();

	  private ImageSize thumbnailSize;

	  private static final Logger log = Logger.getLogger(ImageResizer.class);

	  public ImageResizer(final ImageSize thumbnailSize,
	    final ImageSize[] imageSizes) {
	    directoryFilter = new MultipleFilenameFilter();
	    this.imageSizes = Arrays.asList(imageSizes);
	    NameFilenameFilter photoCacheFilter = new NameFilenameFilter();
	    photoCacheFilter.addName("photoCache");
	    directoryFilter.addExclusionFilter(photoCacheFilter);
	    directoryFilter.addFilter(DirectoryFilenameFilter.FILTER);
	    this.thumbnailSize = thumbnailSize;
	  }

	  private void addImage(final File file) {
	    synchronized (imageQueue) {
	      imageQueue.add(file);
	      imageQueue.notify();
	    }
	  }

	  private File getImage() {
	    synchronized (imageQueue) {
	      if (imageQueue.isEmpty()) {
	        try {
	          imageQueue.wait();
	        } catch (InterruptedException e) {
	          log.warn("Wating for queue interrupted: " + e.getMessage(), e);
	        }
	      }
	    }
	    File file = (File)imageQueue.first();
	    imageQueue.remove(file);
	    return file;
	  }

	  public void scheduleDirectoryResize(final File directory) {
	    addImage(directory);
	  }

	  public void scheduleImageResize(final File file) {
	    addImage(file);
	  }

	  public synchronized void run() {
	    while (true) {
	      try {
	        File file = getImage();
	        if (file.isDirectory()) {
	          log.debug("** Resizing directory " + file);
	          resizeDirectory(file);
	          log.debug("** End directory " + file);
	        } else {
	          log.debug("** Resizing file " + file);
	          resizeImageFile(file);
	          log.debug("** End file " + file);
	        }
	      } catch (IOException e) {
	        log.error("Unable to resize file", e);
	      }
	    }
	  }

	  private void resizeImageFile(final File file) throws IOException {
	    if (!cacheFilesUpToDate(file)) {
	      List files = new ArrayList();
	      for (Iterator sizes = imageSizes.iterator(); sizes.hasNext();) {
	        ImageSize size = (ImageSize)sizes.next();
	        File destFile = getFileName(file, size);
	        files.add(destFile);
	      }
	      log.debug(files);
	      imageAction.thumbnail(file, getFileName(file, thumbnailSize), thumbnailSize);
	      imageAction.scale(file, files, imageSizes);
	    }
	  }

	  private boolean cacheFilesUpToDate(final File file) {
	    for (Iterator sizes = imageSizes.iterator(); sizes.hasNext();) {
	      ImageSize size = (ImageSize)sizes.next();
	      File cacheFile = getFileName(file, size);
	      if (!cacheFile.exists() || cacheFile.length() == 0
	        || cacheFile.lastModified() < file.lastModified()) {
	        return false;
	      }

	    }
	    return true;
	  }

	  private File getFileName(final File file, final ImageSize size) {
	    int width = size.getWidth();
	    File cacheFile = new File(file.getParentFile(), "photoCache/" + width + "/"
	      + FileUtil.getFileNamePrefix(file) + ".jpg");
	    return cacheFile;
	  }

	  public File getCacheFile(final File file, final int size) throws IOException {
	    File cacheFile = new File(file.getParentFile(), "photoCache/" + size + "/"
	      + FileUtil.getFileNamePrefix(file) + ".jpg");
	    if (!cacheFile.exists() || cacheFile.length() == 0
	      || cacheFile.lastModified() < file.lastModified()) {
	      scheduleImageResize(file);
	      return null;
	    }
	    return cacheFile;
	  }

	  public void resizeDirectory(final File directory) throws IOException {
	    log.info(directory);
	    createCache(directory);
	    for (Iterator sizes = imageSizes.iterator(); sizes.hasNext();) {
	      ImageSize size = (ImageSize)sizes.next();
	      cleanPhotoCache(directory, size);
	    }
	    File[] directories = directory.listFiles(directoryFilter);
	    if (directories != null) {
	      for (int i = 0; i < directories.length; i++) {
	        resizeDirectory(directories[i]);
	      }
	    }
	  }

	  private void createCache(final File directory) throws IOException {
	    File[] files = directory.listFiles(ExtensionFilenameFilter.IMAGE_FILTER);
	    if (files != null) {
	      for (int i = 0; i < files.length; i++) {
	        File file = files[i];
	        resizeImageFile(file);
	      }
	    }
	  }

	  private void cleanPhotoCache(final File directory, final ImageSize size) {
	    File cacheDirectory = new File(directory, "photoCache/" + size.getWidth());
	    File[] files = cacheDirectory.listFiles(ExtensionFilenameFilter.IMAGE_FILTER);
	    if (files != null) {
	      for (int i = 0; i < files.length; i++) {
	        File file = files[i];
	        String name = file.getName();
	        if (!new File(directory, name).exists()) {
	          file.delete();
	        }
	      }
	    }

	  }
	}